#pragma once

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "Commen.hpp"
#include "Thread.hpp"
#include "InetAddress.hpp"
#include "UnCopy.hpp"

static const int default_fd = -1;
static const uint16_t default_port = 9090;
static const std::string default_ip = "127.0.0.1";

class ThreadData
{
public:
    ThreadData(int socket_fd, const InetAddress& server)
        : _socket_fd(socket_fd), _server_address(server)
    {}

    ~ThreadData() 
    {}
public:
    int _socket_fd;
    InetAddress _server_address;
};

class UdpClient : public UnCopy
{
public:
    UdpClient(std::string server_ip = default_ip, uint16_t server_port = default_port)
        : _server_ip(server_ip), _server_port(server_port), _socket_fd(default_fd)
    {
        init();
    }

    void init()
    {
        // 1.创建socket
        _socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_socket_fd < 0)
        {
            log.message(FATAL, "socket error, %d: %s\n", errno, strerror(errno));
            exit(SOCKET_ERR);
        }

        // 2.客户端不填写自己的网络信息，因为不做绑定

        // 3.客户端不显式绑定网络信息，udp cilent首次发送数据时，内核随机绑定

        // 4.填写对端网络信息
        bzero(&_server_addr, sizeof(_server_addr));
        _server_addr.sin_family = AF_INET;
        _server_addr.sin_port = htons(_server_port);
        _server_addr.sin_addr.s_addr = inet_addr(_server_ip.c_str());
    }

    void start()
    {
        ThreadData td(_socket_fd, _server_addr);

        Thread<ThreadData> sender("sender", [](ThreadData& td) {
            while(true)
            {
                std::string send_buffer;
                // std::cout << ">> ";
                std::getline(std::cin, send_buffer);
                struct sockaddr_in server = td._server_address.getAddress();
                ssize_t send_n = sendto(td._socket_fd, send_buffer.c_str(), send_buffer.size(), 0, 
                    (struct sockaddr*)&server, sizeof(server));
                if(send_n <= 0)
                {
                    log.message(ERROR, "send error...");
                }
            }
        }, td);
        sender.start();

        Thread<ThreadData> receiver("receiver", [](ThreadData& td) {
            while(true)
            {
                char recv_buffer[4096];
                struct sockaddr_in tmp;
                socklen_t len = sizeof(tmp);
                ssize_t recv_n = recvfrom(td._socket_fd, recv_buffer, sizeof(recv_buffer)-1, 0, (struct sockaddr*)&tmp, &len);
                if(recv_n > 0)
                {
                    recv_buffer[recv_n] = '\0';
                    std::cout << "get message: " << recv_buffer << std::endl;               
                }
            }
        }, td);
        receiver.start();

        sender.join();
        receiver.join();
    }

    ~UdpClient()
    {
        close(_socket_fd);
    }
private:
    int _socket_fd;
    std::string _server_ip;
    uint16_t _server_port;
    struct sockaddr_in _server_addr;
};